import math
import os,re,time
import requests
import threading

class Down(threading.Thread):
    def __init__(self, url, ts_name,threadnum,thread_id):
        self.thread_id = int(thread_id)
        self.threadnum = threadnum
        self.url = url
        self.temp = 'temp/' # 临时下载目录
        self.ts_name = ts_name
        self.baseUrl = self.url.replace('index.m3u8', '')
        self.headers = {
            'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'
        }
        super().__init__()  # 继承父类初始化方法

    def run(self):
        self.down_ts()
        pass

    def mkdir_ts_temp(self):
        work_dir = os.getcwd()
        self.temp = 'temp/'  # 临时下载目录
        # 用来保存ts文件
        file_dir = os.path.join(work_dir, self.temp)
        if not os.path.exists(file_dir):
            os.mkdir(file_dir)
            print("下载目录创建完毕\n")

    def get_ts_name(url):
        headers = {
            'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'
        }
        r = requests.get(url, headers=headers)
        all_data = r.text
        text_string = all_data.split("\n")
        if r.status_code != 200:
            print('请确认地址正确并可以正常下载')
        if text_string[0] != '#EXTM3U':
            print(u"非正确m3u8链接")
        # 带有hls的m3u8文件中获得的是ts信息
        # 包括ts文件名称，以及该文件的持续时间
        # 筛选以.ts结尾的行
        # print(text_string[0])
        ts_name = [i for i in text_string if not i.startswith('#')]
        ts_time = [float(re.findall('[.\d]+', i)[0]) for i in text_string if i.startswith('#EXTINF')]
        # 清除数组空元素
        ts_name = [i for i in ts_name if(len(str(i))!=0)]
        if len(ts_name) != len(ts_time):
            print('m3u8内ts文件和时间戳不匹配')
        return ts_name

    def down_ts(self):
        ts_len = len(self.ts_name)
        step = math.ceil(ts_len / self.threadnum)
        for i in range(step):
            i = self.thread_id * step + i
            try:
                down_url = self.baseUrl + self.ts_name[i]
                print('当前线程id:' + str(self.thread_id) + '开始下载片段' + str(i))
                r = requests.get(down_url, headers=self.headers)
                if r.status_code == 200:
                    file_name = self.temp + '/'+ down_url.split('/')[-1]
                    with open(file_name, 'wb') as f:
                        f.write(r.content)
                # breakpoint()
            except:
                break #中止循环

    def merge_ts(ts_name):
        print('开始合并视频文件')
        file_arr = []
        for i in ts_name:
            file_dir = os.path.join(os.getcwd(), 'temp/',i)
            file_arr.append(file_dir)
        filepath_cat = ' '.join(file_arr)
        cmd_str = 'cat ' + filepath_cat + '> merge.ts'
        os.system(cmd_str)
        print('合并视频文件完成')

if __name__=='__main__':
    link = 'https://ckcdnc1.cdn2020.com/video/m3u8/2020/03/07/c19957d0/index.m3u8a'
    # 线程数
    threadnum = 30
    # 信号量，同时只允许3个线程运行
    threading.BoundedSemaphore(threadnum)
    start_time = time.time()
    # 创建下载目录
    Down.mkdir_ts_temp(threading)
    # 获取ts_name
    ts_name = Down.get_ts_name(link)
    # 获取ts长度
    ts_len = len(ts_name)

    print('这是主线程：', threading.current_thread().name)
    thread_list = []
    for i in range(threadnum):
        t = Down(link,ts_name,threadnum, str(i))
        thread_list.append(t)
    for t in thread_list:
        t.setDaemon(True)  # 设置为守护线程，不会因主线程结束而中断
        t.start()
    for t in thread_list:
        t.join()  # 主线程等待子线程执行完毕后才退出
    Down.merge_ts(ts_name)
    print('主线程结束！', threading.current_thread().name)
    print('主线程结束，一共用时：', time.time() - start_time)